﻿namespace MicroCloud.Entity
{
    /// <summary>
    /// 种子数据初始化基类
    /// </summary>
    /// <typeparam name="TEntity">实体类型</typeparam>
    /// <typeparam name="TKey">实体主键类型</typeparam>
    //[Dependency(ServiceLifetime.Singleton)]
    public abstract class SeedDataInitializerBase<TEntity, TKey> : ISeedDataInitializer
        where TEntity : class, IEntity<TKey>
        where TKey : IEquatable<TKey>
    {
        //字段
        private readonly IServiceProvider _rootProvider;
        private readonly ILogger _logger;
        
        /// <summary>
        /// 初始化一个种子数据初始化基类 <see cref="SeedDataInitializerBase{TEntity, TKey}"/> 的新实例
        /// </summary>
        /// <param name="rootProvider">服务提供者</param>
        protected SeedDataInitializerBase(IServiceProvider rootProvider)
        {
            _rootProvider = rootProvider;
            _logger = rootProvider.GetLogger(GetType());
        }

        /// <summary>
        /// 获取 强制同步，每次服务重新启动后均执行同步
        /// </summary>
        public virtual bool ForceSync => false;

        #region ISeedDataInitializer
        /// <summary>
        /// 获取 初始化的顺序编号
        /// </summary>
        public virtual decimal OrderNo => 0;

        /// <summary>
        /// 获取 所属实体类型
        /// </summary>
        public Type EntityType => typeof(TEntity);

        /// <summary>
        /// 初始化种子数据
        /// </summary>
        public void Initialize()
        {
            _rootProvider.ExecuteScopedWork(provider =>
            {
                TEntity[] entities = SeedData(provider);
                if (entities?.Length > 0)
                {
                    var requireSynch = ForceSync;
                    if (!ForceSync)
                    {
                        requireSynch = entities.CheckSyncByHash(provider, _logger, typeof(TEntity), GetType(), GetType().GetDescription(), $"初始化{typeof(TEntity).GetDescription()}数据");
                    }

                    if (!requireSynch)
                    {
                        _logger.LogInformation("同步种子数据 {type} 到数据库时，验证数据签名与上次相同，跳过同步。", GetType());
                        return;
                    }

                    SyncToDatabase(entities, provider);
                    _logger.LogInformation("同步 {length} 个 {type} 种子数据到数据库", entities.Length, typeof(TEntity));
                }
            });
        }

        #endregion

        /// <summary>
        /// 重写以提供要初始化的种子数据
        /// </summary>
        /// <param name="provider">服务提供者</param>
        /// <returns></returns>
        protected abstract TEntity[] SeedData(IServiceProvider provider);

        /// <summary>
        /// 重写以提供判断某个实体是否存在的表达式
        /// </summary>
        /// <param name="entity">要判断的实体</param>
        /// <returns></returns>
        protected abstract Expression<Func<TEntity, bool>> ExistingExpression(TEntity entity);
      
        /// <summary>
        /// 将种子数据初始化到数据库
        /// </summary>
        /// <param name="entities">实体对象数组</param>
        /// <param name="provider">服务提供者</param>
        protected virtual void SyncToDatabase(TEntity[] entities, IServiceProvider provider)
        {
            if (entities == null || entities.Length == 0) return;

            provider.BeginUnitOfWorkTransaction(provider =>
            {
                IRepository<TEntity, TKey> repository = provider.GetService<IRepository<TEntity, TKey>>();
                foreach (TEntity entity in entities)
                {
                    if (repository.CheckExists(ExistingExpression(entity)))
                    {
                        continue;
                    }
                    var result = repository.Insert(entity);
                    if (!result.Succeeded)
                    {
                        throw new Exception(I18N.T("同步 {0} 种子数据 {1} 时出错：{2}", typeof(TEntity), entity?.ToJsonString(), result.Message));
                    }
                }
            });

        }
    }
}